import { _decorator, Camera, CCBoolean, CCFloat, CCInteger, CCString, Enum, RenderTexture } from 'cc';
import { SensorComponent } from './SensorComponent';
import { StackingSensor } from './StackingSensor';
import { SensorCompressionType } from './CompressionSpec';
import { ISensor, ObservationType } from './ISensor';
import { CameraSensor } from './CameraSensor';
const { ccclass, property, menu } = _decorator;

Enum(ObservationType);
Enum(SensorCompressionType);

@ccclass('CameraSensorComponent')
@menu('ML Agents/Camera Sensor')
export class CameraSensorComponent extends SensorComponent {
	@property({ type: Camera, serializable: true, visible: true })
	private _camera: Camera = null!;
	public get camera(): Camera {
		return this._camera;
	}
	public set camera(value: Camera) {
		this._camera = value;
	}

	_sensor: CameraSensor = null!;

	@property(CCString)
	sensorName: string = 'CameraSensor';

	@property({ type: CCFloat, serializable: true, visible: true })
	private _width: number = 84;
	public get width(): number {
		return this._width;
	}
	public set width(value: number) {
		this._width = value;
	}

	@property({ type: CCFloat, serializable: true, visible: true })
	private _height: number = 84;
	public get height(): number {
		return this._height;
	}
	public set height(value: number) {
		this._height = value;
	}

	@property({ serializable: true, visible: true })
	private _grayScale: boolean = false;
	public get grayScale(): boolean {
		return this._grayScale;
	}
	public set grayScale(value: boolean) {
		this._grayScale = value;
	}

	@property({ range: [1, 50, 1] })
	public observationStacks = 1;

	@property({ type: ObservationType, serializable: true, visible: true })
	private _observationType: ObservationType = ObservationType.Default;
	public get observationType(): ObservationType {
		return this._observationType;
	}
	public set observationType(value: ObservationType) {
		this._observationType = value;
		this.updateSensor();
	}

	@property({ type: ObservationType, serializable: true, visible: true })
	private _runtimeCameraEnable: boolean = false;
	public get runtimeCameraEnable(): boolean {
		return this._runtimeCameraEnable;
	}
	public set runtimeCameraEnable(value: boolean) {
		this._runtimeCameraEnable = value;
	}

	private _renderTexture: RenderTexture = null!;
	public get renderTexture(): RenderTexture {
		return this._renderTexture;
	}
	public set renderTexture(value: RenderTexture) {
		this._renderTexture = value;
	}

	@property({ type: SensorCompressionType, serializable: true, visible: true })
	private _compression: SensorCompressionType = SensorCompressionType.PNG;
	public get compression(): SensorCompressionType {
		return this._compression;
	}
	public set compression(value: SensorCompressionType) {
		this._compression = value;
		this.updateSensor();
	}

	createSensors(): ISensor[] {
		const renderTexture = new RenderTexture();
		renderTexture.reset({
			width: this._width,
			height: this._height,
		});

		this._camera.targetTexture = renderTexture;
		this._sensor = new CameraSensor(
			this._camera,
			this._width,
			this._height,
			this.grayScale,
			this.sensorName,
			this._compression,
			this.observationType
		);
		if (this.observationStacks !== 1) {
			return [new StackingSensor(this._sensor, this.observationStacks)];
		}

		return [this._sensor];
	}

	private updateSensor() {
		if (this._sensor != null) {
			this._sensor.camera = this._camera;
			this._sensor.camera.enabled = this._runtimeCameraEnable;
			this._sensor.compressionType = this._compression;
		}
	}

	getSensor(): ISensor {
		return this._sensor;
	}

	protected onDestroy(): void {
		this._sensor.dispose();
		this._sensor = null!;
	}
}
